use std::{
    collections::{HashMap, HashSet},
    fmt::Display,
};
pub mod impls;

pub trait Validator<Data> {
    type Error: Display;

    fn validate(
        &self,
        ident: Option<&str>,
        name: Option<&str>,
        data: &Data,
    ) -> Result<(), Self::Error>;
}

pub trait SelfValidator {
    type Error: Display;

    fn validate(&self, _ident: Option<&str>, _name: Option<&str>) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<T: SelfValidator> SelfValidator for Option<T> {
    type Error = T::Error;
    fn validate(&self, ident: Option<&str>, name: Option<&str>) -> Result<(), Self::Error> {
        if let Some(value) = self {
            value.validate(ident, name)?;
        }
        Ok(())
    }
}

impl<Data, T: Validator<Data>> Validator<Option<Data>> for T {
    type Error = T::Error;

    fn validate(
        &self,
        ident: Option<&str>,
        name: Option<&str>,
        data: &Option<Data>,
    ) -> Result<(), Self::Error> {
        if let Some(data) = data {
            self.validate(ident, name, data)?;
        }
        Ok(())
    }
}

impl<T: SelfValidator> SelfValidator for Vec<T> {
    type Error = T::Error;

    fn validate(&self, ident: Option<&str>, name: Option<&str>) -> Result<(), Self::Error> {
        for item in self {
            item.validate(ident, name)?;
        }
        Ok(())
    }
}

impl<T: SelfValidator> SelfValidator for HashSet<T> {
    type Error = T::Error;

    fn validate(&self, ident: Option<&str>, name: Option<&str>) -> Result<(), Self::Error> {
        for item in self {
            item.validate(ident, name)?;
        }
        Ok(())
    }
}

impl<K, V: SelfValidator> SelfValidator for HashMap<K, V> {
    type Error = V::Error;

    fn validate(&self, ident: Option<&str>, name: Option<&str>) -> Result<(), Self::Error> {
        for v in self.values() {
            v.validate(ident, name)?;
        }
        Ok(())
    }
}
